const mongoose = require('mongoose')
const bcrypt = require('bcryptjs')
const Schema = mongoose.Schema
const Mixed = Schema.Types.Mixed //可以存储任何类型的数据
const SALT_WORK_FACTOR = 10
const MAX_LOGIN_ATTEMPTS = 5 //尝试5次
const LOCK_TIME = 2 * 60 * 60 * 1000 //锁定2小时

const userSchema = new Schema({
    userName: {
        unique: true,
        required : true,
        type: String
    },
    email: {
        unique: true,
        type: String
    },
    password: {
        unique: false,
        required : true,
        type: String
    },
    loginAttempts: {
    	type: Number,
    	required : true,
    	default : 0
    },
    lockUntil: Number,
    meta: {
        createdAt: {
            type: Date,
            default: Date.now()
        },
        updatedAt: {
            type: Date,
            default: Date.now()
        }
    }
})

userSchema.virtual('isLocked').get(function() {
    return !!(this.lockUntil && this.lockUntil > Date.now())
})

//mongoDB在save之前的一个中间件的操作
userSchema.pre('save', function(next) {
    if (this.isNew) {
        this.meta.createdAt = this.meta.updatedAt = Date.now()
    } else {
        this.meta.updatedAt = Date.now()
    }
    next()
})

userSchema.pre('save', function(next) {
    if (!this.isModified('password')) return next()

    bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
        if (err) return next(err)

        bcrypt.hash(user.password, salt, (error, hash) => {
            if (error) return next(error)

            this.password = hash
            next()
        })
    })
    next()
})

//添加密码比较方法
userSchema.methods = {
    comparePassword: (_password, password) => {
        return new Promise((resolve, reject) => {
            bcrypt.compare(_password, password, (err, isMatch) => {
                if (!err) resolve(isMatch)
                else reject(err)
            })
        })
    },

    //判断用户是否超过次数被锁定
    //如果过了锁定时间，解锁
    incLoginAttepts: (user) => {
        return new Promise((resolve, reject) => {
            if (this.lockUntil && this.lockUntil < Date.now()) {
                this.update({
                    $set: {
                        loginAttempts: 1
                    },
                    $unset: {
                        lockUntil: 1
                    }
                }, (err) => {
                    if (!err) resolve(true)
                    else reject(err)
                })
            }else {
            	let updates = {
            		$inc: {
            			loginAttempts: 1
            		}
            	}

            	if(this.loginAttempts + 1 >= MAX_LOGIN_ATTEMPTS && !this.isLocked) {
            		updates.$set = {
            			lockUntil: Date.now() + LOCK_TIME
            		}
            	}

            	this.update(updates, err => {
            		if(!err) return resolve(true)
            		else reject(err)
            	})

            }
        })

    }
}

mongoose.model('User', userSchema)